<?php

namespace helper;

class Utils
{

    /**
     * 是否手机
     * @return bool
     */
    static public function isMobile()
    {
        return request()->isMobile();
    }

    /**
     * 是否为微信内部浏览器
     * @return bool
     */
    static public function isWeBrowser()
    {
        return (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false);
    }

    /**
     * 删除html
     * @param $str
     * @return string
     */
    static public function delHtml($str)
    {
        return trim(strip_tags($str));
    }

    /**
     * 输出安全html
     * @param string $str 需要操作的字符串
     * @return string 操作后的字符串
     */
    static public function htmlOut($str)
    {
        $str = stripslashes(htmlspecialchars_decode($str));
        $str = preg_replace(["'<script[^>]*?>.*?</script>'si", "'<iframe[^>]*?>.*?</iframe>'si"], '', $str); //过滤script和iframe
        return $str;
    }

    /**
     * 内容分页
     * @param string $content 需要拆分的数据
     * @param string $needle 拆分后的数据
     */
    static public function contentPage($content, $needle = '_page_break_tag_')
    {
        //根据分页标识来拆分分页
        $pageContent    = explode($needle, self::htmlOut($content));
        $page           = isset($_GET['cp']) ? intval($_GET['cp']) : 0;
        $contentNowPage = $page > 0 ? $page - 1 : 0;
        echo $pageContent[$contentNowPage]; //输出当前页内容
        $currentUrl = self::currentUrl();
        $url        = strpos($currentUrl, '.html?') !== false ? $currentUrl . '&' : $currentUrl . '?';
        if (($pageCount = count($pageContent)) > 1) {
            $pageHtml = '<div class="clear"></div><div class="page">#page#</div>';
            $pageStr  = [];
            for ($i = 1; $i <= $pageCount; $i++) {
                $style = '';
                if ($page == $i) {
                    $style = 'class="current"';
                }
                $pageStr[] = '<a href="' . $url . 'cp=' . $i . '" ' . $style . '>' . $i . '</a>';
            }
            $pageHtml = str_replace('#page#', implode(PHP_EOL, $pageStr), $pageHtml);
            echo $pageHtml;
        }
    }

    /**
     * 获取当前url
     * @return string 当前url
     */
    static public function currentUrl()
    {
        $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
        return "{$protocol}{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
    }

    /**
     * 友好的时间显示
     * @param int $sTime 待显示的时间
     * @param string $type 类型. normal | mohu | full | ymd | other
     * @return string
     */
    static public function friendDate($sTime, $type = 'normal')
    {
        if (!$sTime) return '';
        //sTime=源时间，cTime=当前时间，dTime=时间差
        $cTime = time();
        $dTime = $cTime - $sTime;
        $dDay  = intval(date("z", $cTime)) - intval(date("z", $sTime));
        //$dDay     =   intval($dTime/3600/24);
        $dYear = intval(date("Y", $cTime)) - intval(date("Y", $sTime));
        //normal：n秒前，n分钟前，n小时前，日期
        if ($type == 'normal') {
            if ($dTime < 60) {
                if ($dTime < 10) {
                    return '刚刚';    //by yangjs
                } else {
                    return intval(floor($dTime / 10) * 10) . "秒前";
                }
            } elseif ($dTime < 3600) {
                return intval($dTime / 60) . "分钟前";
                //今天的数据.年份相同.日期相同.
            } elseif ($dYear == 0 && $dDay == 0) {
                //return intval($dTime/3600)."小时前";
                return '今天' . date('H:i', $sTime);
            } elseif ($dYear == 0) {
                return date("m月d日 H:i", $sTime);
            } else {
                return date("Y-m-d H:i", $sTime);
            }
        } elseif ($type == 'mohu') {
            if ($dTime < 60) {
                return $dTime . "秒前";
            } elseif ($dTime < 3600) {
                return intval($dTime / 60) . "分钟前";
            } elseif ($dTime >= 3600 && $dDay == 0) {
                return intval($dTime / 3600) . "小时前";
            } elseif ($dDay > 0 && $dDay <= 7) {
                return $dDay . "天前";
            } elseif ($dDay > 7 && $dDay <= 30) {
                return intval($dDay / 7) . '周前';
            } elseif ($dDay > 30) {
                return intval($dDay / 30) . '个月前';
            }
            //full: Y-m-d , H:i:s
        } elseif ($type == 'full') {
            return date("Y-m-d , H:i:s", $sTime);
        } elseif ($type == 'ymd') {
            return date("Y-m-d", $sTime);
        } else {
            if ($dTime < 60) {
                return $dTime . "秒前";
            } elseif ($dTime < 3600) {
                return intval($dTime / 60) . "分钟前";
            } elseif ($dTime >= 3600 && $dDay == 0) {
                return intval($dTime / 3600) . "小时前";
            } elseif ($dYear == 0) {
                return date("Y-m-d H:i:s", $sTime);
            } else {
                return date("Y-m-d H:i:s", $sTime);
            }
        }
        return '';
    }

    /**
     * 人民币小写转大写
     * @param int $number 数值
     * @param string $int_unit 币种单位，默认"元"，有的需求可能为"圆"
     * @param bool $is_round 是否对小数进行四舍五入
     * @param bool $is_extra_zero 是否对整数部分以0结尾，小数存在的数字附加0,比如1960.30
     * @return string
     */
    static public function num2rmb($number = 0, $int_unit = '元', $is_round = TRUE, $is_extra_zero = FALSE)
    {
        // 将数字切分成两段
        $parts = explode('.', $number, 2);
        $int   = isset($parts[0]) ? strval($parts[0]) : '0';
        $dec   = isset($parts[1]) ? strval($parts[1]) : '';

        // 如果小数点后多于2位，不四舍五入就直接截，否则就处理
        $dec_len = strlen($dec);
        if (isset($parts[1]) && $dec_len > 2) {
            $dec = $is_round
                ? substr(strrchr(strval(round(floatval("0." . $dec), 2)), '.'), 1)
                : substr($parts[1], 0, 2);
        }

        // 当number为0.001时，小数点后的金额为0元
        if (empty($int) && empty($dec)) {
            return '零';
        }

        // 定义
        $chs     = array('0', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖');
        $uni     = array('', '拾', '佰', '仟');
        $dec_uni = array('角', '分');
        $exp     = array('', '万');
        $res     = '';

        // 整数部分从右向左找
        for ($i = strlen($int) - 1, $k = 0; $i >= 0; $k++) {
            $str = '';
            // 按照中文读写习惯，每4个字为一段进行转化，i一直在减
            for ($j = 0; $j < 4 && $i >= 0; $j++, $i--) {
                $u   = $int[$i] > 0 ? $uni[$j] : ''; // 非0的数字后面添加单位
                $str = $chs[$int[$i]] . $u . $str;
            }
            //echo $str."|".($k - 2)."<br>";
            $str = rtrim($str, '0');// 去掉末尾的0
            $str = preg_replace("/0+/", "零", $str); // 替换多个连续的0
            if (!isset($exp[$k])) {
                $exp[$k] = $exp[$k - 2] . '亿'; // 构建单位
            }
            $u2  = $str != '' ? $exp[$k] : '';
            $res = $str . $u2 . $res;
        }

        // 如果小数部分处理完之后是00，需要处理下
        $dec = rtrim($dec, '0');

        // 小数部分从左向右找
        if (!empty($dec)) {
            $res .= $int_unit;

            // 是否要在整数部分以0结尾的数字后附加0，有的系统有这要求
            if ($is_extra_zero) {
                if (substr($int, -1) === '0') {
                    $res .= '零';
                }
            }

            for ($i = 0, $cnt = strlen($dec); $i < $cnt; $i++) {
                $u   = $dec[$i] > 0 ? $dec_uni[$i] : ''; // 非0的数字后面添加单位
                $res .= $chs[$dec[$i]] . $u;
            }
            $res = rtrim($res, '0');// 去掉末尾的0
            $res = preg_replace("/0+/", "零", $res); // 替换多个连续的0
        } else {
            $res .= $int_unit . '整';
        }
        return $res;
    }

    /**
     * 抓取远程图片
     * @param string $url 远程图片地址
     * @param string $filename 保存到本地地址 默认按时间戳微妙数MD5生成
     * @param string $save_dir 保存路径 默认upload/images/Ymd/ 路径
     * @return array 抓取后的数据数组
     */
    static public function saveImage(string $url, $filename = '', $save_dir = '')
    {
        if (trim($url) == '') {
            return ['msg' => '远程地址不能为空！', 'code' => 0];
        }
        if (trim($save_dir) == '') {
            $save_dir = './upload/' . date('Ymd') . '/';
            //创建保存目录
            if (!file_exists($save_dir) && !mkdir($save_dir, 0777, true)) {
                return ['msg' => '文件目录不可写！', 'code' => 0];
            }
            Files::createDir($save_dir); //递归创建保存文件夹
        }
        if (trim($filename) == '') {
            //文件头获取文件名
            $mimes = [
                'image/bmp'    => '.bmp',
                'image/gif'    => '.gif',
                'image/jpeg'   => '.jpg',
                'image/png'    => '.png',
                'image/x-icon' => '.ico',
            ];
            if (($headers = get_headers($url, 1)) !== false) {
                $type = $headers['Content-Type'];
                $ext  = $mimes[$type];
                if ($ext != '.gif' && $ext != '.jpg' && $ext != '.png' && $ext != '.jpeg') {
                    return ['msg' => '文件格式不正确！', 'code' => 0];
                }
            } else {
                return ['msg' => '未找到文件头！', 'code' => 0];
            }
            $filename = md5(microtime(true)) . $ext;
        }
        ob_start();
        readfile($url);
        $img = ob_get_contents();
        ob_end_clean();
        //文件大小
        $fp2 = @fopen($save_dir . $filename, 'a');
        fwrite($fp2, $img);
        fclose($fp2);
        unset($img, $url);
        return ['file_name' => $filename, 'save_path' => str_replace("./upload/","/upload/",$save_dir) . $filename];
    }

    /**
     * 图片转base64
     * @param string $url
     * @return string
     */
    static public function image2Base64($url = '')
    {
        $imageInfo = getimagesize($url);
        return 'data:' . $imageInfo['mime'] . ';base64,' . chunk_split(base64_encode(file_get_contents($url)));
    }

    /**
     * base64图片转存
     * @param $base64
     * @param $fileName
     * @return bool|string
     */
    static public function base642image($base64, $fileName)
    {
        //匹配出图片的格式
        if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64, $result)) {
            $type     = $result[2];
            $pathInfo = pathinfo($fileName);
            $path     = app()->getRootPath() . 'public/' . $pathInfo['dirname'];
            if (!file_exists($path)) Files::createDir($path);
            $file = "{$pathInfo['filename']}.{$type}";
            if (file_put_contents($path . '/' . $file, base64_decode(str_replace($result[1], '', $base64)))) {
                return '/' . $pathInfo['dirname'] . '/' . $file;
            }
        }
        return false;
    }

    /**
     * 把从HTML源码中获取的相对路径转换成绝对路径
     * @param string $url HTML中获取的网址
     * @param string $baseUrl 用来参考判断的原始地址
     * @return bool|false|string|string[]|null 返回修改过的网址，如果网址有误则返回false
     */
    static public function filterRelativeUrl($url, $baseUrl)
    {
        //STEP1: 先去判断URL中是否包含协议，如果包含说明是绝对地址则可以原样返回
        if (strpos($url, '://') !== false) return $url;
        //STEP2: 解析传入的URI
        if (substr($baseUrl, -1) == '/') $baseUrl .= 'index.html'; //最后一位为/时人为补充一个文件名称
        $urlPart = parse_url($baseUrl);
        if ($urlPart == FALSE) return false;
        $rootUrl = $urlPart['scheme'] . '://' . $urlPart['host'] . (isset($urlPart['port']) ? ':' . $urlPart['port'] : '');
        //STEP3: 如果URL以左斜线开头，表示位于根目录
        if (strpos($url, '/') === 0) {
            return $rootUrl . $url;
        }
        //STEP4: 不位于根目录，也不是绝对路径，考虑如果不包含'./'的话，需要把相对地址接在原URL的目录名上
        $urlDir = (isset($urlPart['path']) && $urlPart['path']) ? '/' . ltrim(dirname($urlPart['path']), '/') : '';
        if (strpos($url, './') === FALSE) {
            if ($urlDir != '') {
                return $rootUrl . $urlDir . '/' . $url;
            } else {
                return $rootUrl . '/' . $url;
            }
        }
        //STEP5: 如果相对路径中包含'../'或'./'表示的目录，需要对路径进行解析并递归
        //STEP5.1: 把路径中所有的'./'改为'/'，'//'改为'/'
        $url = preg_replace('/[^\.]\.\/|\/\//', '/', $url);
        if (strpos($url, './') === 0)
            $url = substr($url, 2);
        //STEP5.2: 使用'/'分割URL字符串以获取目录的每一部分进行判断
        $urlFullDir = ltrim($urlDir . '/' . $url, '/');
        $urlArr     = explode('/', $urlFullDir);
        if ($urlArr[0] == '..') return false;
        //因为数组的第一个元素不可能为'..'，所以这里从第二个元素开始循环
        $dstArr = $urlArr;  //拷贝一个副本，用于最后组合URL
        for ($i = 1; $i < count($urlArr); $i++) {
            if ($urlArr[$i] == '..') {
                $j = 1;
                while (TRUE) {
                    if (isset($dstArr[$i - $j]) && $dstArr[$i - $j] != false) {
                        $dstArr[$i - $j] = false;
                        $dstArr[$i]      = false;
                        break;
                    } else {
                        $j++;
                    }
                }
            }
        }
        // 组合最后的URL并返回
        $dstStr = $rootUrl;
        foreach ($dstArr as $val) {
            if ($val != FALSE) $dstStr .= '/' . $val;
        }
        return $dstStr;
    }

    /**
     * 把秒数转换为时分秒的格式
     * @param $times
     * @return string
     */
    static public function secToTime($times)
    {
        $result = '00:00:00';
        if ($times > 0) {
            $hour   = floor($times / 3600);
            $minute = floor(($times - 3600 * $hour) / 60);
            $second = floor((($times - 3600 * $hour) - 60 * $minute) % 60);
            $result = sprintf("%02d", $hour) . ':' . sprintf("%02d", $minute) . ':' . sprintf("%02d", $second);
        }
        return $result;
    }

    /**
     * 格式化价格 保留到两位小数
     * @param $price
     * @param int $digits 保留位数
     * @return string
     */
    static public function formatPrice($price, $digits = 2)
    {
        return sprintf("%.{$digits}f", $price);
    }

    /**
     * 对表情符号进行处理入库
     * @param string $content
     * @return string
     */
    static public function encodeLook($content = '')
    {
        return preg_replace_callback('/[\xf0-\xf7].{3}/', function ($r) {
            return '@E' . base64_encode($r[0]);
        }, $content);
    }

    /**
     * 返回内容的时候对 使用encodeLook加密过的表情进行转换
     * @param string $content
     * @return string
     */
    static public function decodeLook($content = '')
    {
        return preg_replace_callback('/@E(.{6}==)/', function ($r) {
            return base64_decode($r[1]);
        }, $content);
    }

    /**
     * authcode加密函数
     * @param $str //加减密字符串
     * @param string $operation
     * @param string $key
     * @param int $expiry
     * @return bool|string
     */
    static public function authcode($str, $operation = 'DECODE', $key = 'DmoZ2CeT', $expiry = 0)
    {
        $m   = ['+' => '75Rym', '/' => '9UqW3'];
        $str = str_replace(array_values($m), array_keys($m), $str);
        // 动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙
        $ckey_length = 4;
        // 密匙
        $key = md5($key);
        // 密匙a会参与加解密
        $keya = md5(substr($key, 0, 16));
        // 密匙b会用来做数据完整性验证
        $keyb = md5(substr($key, 16, 16));
        // 密匙c用于变化生成的密文
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($str, 0, $ckey_length) :
            substr(md5(microtime()), -$ckey_length)) : '';
        // 参与运算的密匙
        $cryptKey   = $keya . md5($keya . $keyc);
        $key_length = strlen($cryptKey);
        // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，
        //解密时会通过这个密匙验证数据完整性
        // 如果是解码的话，会从第$ckey_length位开始，因为密文前$ckey_length位保存 动态密匙，以保证解密正确
        $str           = $operation == 'DECODE' ? base64_decode(substr($str, $ckey_length)) :
            sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($str . $keyb), 0, 16) . $str;
        $string_length = strlen($str);
        $result        = '';
        $box           = range(0, 255);
        $rickey        = array();
        // 产生密匙簿
        for ($i = 0; $i <= 255; $i++) {
            $rickey[$i] = ord($cryptKey[$i % $key_length]);
        }
        // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度
        for ($j = $i = 0; $i < 256; $i++) {
            $j       = ($j + $box[$i] + $rickey[$i]) % 256;
            $tmp     = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        // 核心加解密部分
        for ($a = $j = $i = 0; $i < $string_length; $i++) {
            $a       = ($a + 1) % 256;
            $j       = ($j + $box[$a]) % 256;
            $tmp     = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            // 从密匙簿得出密匙进行异或，再转成字符
            $result .= chr(ord($str[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }
        if ($operation == 'DECODE') { //解密
            // 验证数据有效性，请看未加密明文的格式
            if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) &&
                substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else { //加密
            // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因
            // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码
            $result = str_replace('=', '', base64_encode($result));
            $result = str_replace(array_keys($m), array_values($m), $result); //去除混淆字符
            return $keyc . $result;
        }
    }

    /**
     * 字符串*号替换
     * @param $string //需要替换的字符串
     * @param $start //开始的保留几位
     * @param $end //最后保留几位
     * @return string
     */
    static public function strMiddleReplace($string, $start, $end)
    {
        return substr($string, 0, $start) . "****" . substr($string, -1 * $end, $end);
    }

    /**
     * 字符串截取，支持中文和其他编码
     * @param $str //源字符串
     * @param $start //从哪里开始
     * @param $length //截取的长度
     * @param string $charset 编码，默认utf-8
     * @param bool $suffix 尾部是否添加···,默认添加
     * @return bool|string 截取好的字符串
     */
    static public function msubstr($str, $start, $length, $charset = "utf-8", $suffix = true)
    {
        if (function_exists("mb_substr"))
            $slice = mb_substr($str, $start, $length, $charset);
        elseif (function_exists('iconv_substr')) {
            $slice = iconv_substr($str, $start, $length, $charset);
            if (false === $slice) {
                $slice = '';
            }
        } else {
            $re['utf-8']  = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
            $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
            $re['gbk']    = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
            $re['big5']   = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
            preg_match_all($re[$charset], $str, $match);
            $slice = join("", array_slice($match[0], $start, $length));
        }
        if ($suffix and mb_strlen($str, 'utf8') > $length) {
            return $slice . '...';
        } else {
            return $slice;
        }
    }

    /**
     * 提取字符串内容中的图片地址
     * @param string $str HTML或者文章
     * @param array $blacklist 图片地址黑名单，排除图片地址中包含该数据的地址；例如 传入 img.baidu.com
     * @param int $number 共抽取 $number 张；默认为0，为0则抽取到最后;
     * @param string $order 顺取倒取； 默认为 asc ，从正方向计数。 desc 从反方向计数
     * @return array|string
     */
    static public function extractImg($str, $blacklist = [], $number = 0, $order = 'asc')
    {
        preg_match_all("/<img.*>/isU", $str, $ereg);
        $img = $ereg[0];
        if ($order != 'asc') $img = array_reverse($img);
        $imgSrcArr = [];
        foreach ($img as $key => $value) {
            $imgSrc    = $value;
            $pregModel = "/src=('|\")(.*)('|\")/isU";
            preg_match_all($pregModel, $imgSrc, $img1);
            if(empty($blacklist)){
                $imgSrcArr[] = $img1[2][0];
            }else{
                if (is_array($blacklist)) {
                    $blacklistBool = true;
                    foreach ($blacklist as $v) {
                        if (strpos($img1[2][0], $v) !== false) $blacklistBool = false;
                    }
                    if ($blacklistBool) {
                        $imgSrcArr[] = $img1[2][0];
                    }
                } else {
                    if (strpos($img1[2][0], $blacklist) === false) {
                        $imgSrcArr[] = $img1[2][0];
                    }
                }
            }
        }
        $imgSrcArr = array_unique($imgSrcArr);
        if ($number > 0) {
            return array_slice($imgSrcArr, 0, $number);
        } else {
            return array_slice($imgSrcArr, 0);
        }
    }

    /**
     * 远程图片转本地
     * @param $content
     * @param array $blackList
     * @return string
     */
    static public function remoteToLocal($content,$blackList=[]){
        $images = Utils::extractImg($content,$blackList);
        foreach($images as $img){
            if(substr($img,0,4)=='http'){
                $ret = Utils::saveImage($img);
                $content = str_replace($img,$ret['save_path'],$content);
            }
        }
        return $content;
    }

    /**
     * textarea文本转化数组
     * @param $str
     * @return array
     */
    static public function textarea2Arr($str)
    {
        if (empty($str)) return [];
        $str = nl2br($str);
        $str = explode("<br />", $str);
        $arr = [];
        foreach ($str as $v) {
            $arr[] = trim($v);
        }
        return $arr;
    }

    /**
     * 生成唯一订单号
     * @param string $prefix
     * @return string
     */
    static public function builderOrderSn($prefix = '')
    {
        return $prefix . date('Ymd') .
               substr(microtime(), 3, 5) .
               substr(implode('',
                   array_map('ord', str_split(substr(uniqid($prefix), 7, 13)))
               ), 0, 7);
    }

    /**
     * 生成UUID
     * @param bool $strtoupper 是否转换为大小写输出
     * @param string $spilt 链接字符 false表示不连接
     * @return string
     * @throws \Exception
     */
    static public function uuid($strtoupper = false, $spilt = '-')
    {
        $str = uniqid('', true);
        $arr = explode('.', $str);
        $str = base_convert($arr[0], 16, 36) .
               base_convert($arr[1], 10, 36) .
               base_convert(bin2hex(random_bytes(5)), 16, 36);
        $len = 24;
        $str = substr($str, 0, $len);
        if (strlen($str) < $len) {
            $mt  = base_convert(bin2hex(random_bytes(5)), 16, 36);
            $str = $str . substr($mt, 0, $len - strlen($str));
        }
        if ($spilt) {
            $str = substr($str, 0, 5) . $spilt .
                   substr($str, 5, 5) . $spilt .
                   substr($str, 15, 5) . $spilt .
                   substr($str, 20, $len);
        }
        return $strtoupper ? strtoupper($str) : $str;
    }

    /**
     * 获取指定长度的随机字母数字组合的字符串
     * @param int $length
     * @param $type
     * @param string $addChars
     * @return string
     */
    static public function random($length = 6, $type = null, $addChars = '')
    {
        $str = '';
        switch ($type) {
            case 0:
                $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' . $addChars;
                break;
            case 1:
                $chars = str_repeat('0123456789', 3);
                break;
            case 2:
                $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars;
                break;
            case 3:
                $chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars;
                break;
            case 4:
                $chars = "们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书" . $addChars;
                break;
            default:
                $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars;
                break;
        }
        if ($length > 10) {
            $chars = $type == 1 ? str_repeat($chars, $length) : str_repeat($chars, 5);
        }
        if ($type != 4) {
            $chars = str_shuffle($chars);
            $str   = substr($chars, 0, $length);
        } else {
            for ($i = 0; $i < $length; $i++) {
                $str .= mb_substr($chars, floor(mt_rand(0, mb_strlen($chars, 'utf-8') - 1)), 1);
            }
        }
        return $str;
    }

    /**
     * 生成一定数量的随机数，并且不重复
     * @param integer $number 数量
     * @param int $length
     * @param int $mode
     * @return mixed
     */
    static public function buildCountRand($number, $length = 4, $mode = 1)
    {
        if (1 == $mode && $length < strlen($number)) {
            //不足以生成一定数量的不重复数字
            return false;
        }
        $rand = array();
        for ($i = 0; $i < $number; $i++) {
            $rand[] = self::random($length, $mode);
        }
        $unqiue = array_unique($rand);
        if (count($unqiue) == count($rand)) {
            return $rand;
        }
        $count = count($rand) - count($unqiue);
        for ($i = 0; $i < $count * 3; $i++) {
            $rand[] = self::random($length, $mode);
        }
        return array_slice(array_unique($rand), 0, $number);
    }

    /**
     * 截取两个字符串之间数据
     * @param $begin
     * @param $end
     * @param $str
     * @return false|string
     */
    static public function cut($begin,$end,$str){
        $b = mb_strpos($str,$begin) + mb_strlen($begin);
        $e = mb_strpos($str,$end) - $b;
        return mb_substr($str,$b,$e);
    }

    /**
     * 是否win系统
     * @return bool
     */
    static public function isWin()
    {
        return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
    }

    /**
     * 提取两个字符串之间字符
     * @param $content
     * @param string $left
     * @param string $right
     * @return array
     */
    static public function extractBetween($content,$left='{',$right='}'){
        preg_match_all ("|{$left}(.*){$right}|U", $content, $ret);
        if(!empty($ret[0])) return $ret;
        return [];
    }

}
